﻿using System.Net;
using System.Text;
using HslCommunication;
using HslCommunication.Profinet.Keyence;
using NetCore8Demo.Models.PLC;

namespace NetCore8Demo.ViewModels.PLC;

/// <summary>
/// 基恩士PLC MCBinary
/// </summary>
public class KeyencePlcBinary : PlcReadAndWriteDemo
{
    private KeyenceMcNet _plc = new();

    #region 单例模式  多个PLC就将构造函数的private改成public
    private static bool _isReflection = true;
    private static object _o = new ();
    // 1. 构造函数私有化
    public KeyencePlcBinary()
    {
        HslCommunication.Authorization.SetAuthorizationCode("b23b00e2-ce46-4bfc-b33c-71c47c2c11c2");
        lock (_o)
        {
            if (_isReflection)
            {
                _isReflection = false;
            }
            else
            {
                throw new Exception("不可以从外界创建");
            }
        }
    }

    // 2. 写一个私有的字段 volatile避免重排的问题
    private static volatile KeyencePlcBinary? _keyencePlcBinary;

    // 3. 写一个方法暴露给外界这个实例
    public static KeyencePlcBinary GetKeyencePLC_Binary()
    {
        // lock是一个互斥锁，Monitor.Enter() Monitor.Exit()
        if (_keyencePlcBinary == null)
        {
            lock (_o)
            {
                if (_keyencePlcBinary == null)
                {
                    // new KeyencePLC_Binary()
                    // 1. 开辟一块内存空间
                    // 2. 创建一个对象
                    // 3. 将对象指向内存空间
                    // 123  132 指针重排
                    _keyencePlcBinary = new KeyencePlcBinary();
                }
            }
        }
        return _keyencePlcBinary;
    }


    #endregion


    #region 连接相关

    #region 连接相关

    public override OperateResult ConnPlc(string ip, int port)
    {
        OperateResult operateResult = new OperateResult();
        if (!IPAddress.TryParse(ip, out _))
        {
            operateResult.IsSuccess = false;
            operateResult.Message = "IP 解析失败";
            return operateResult;
        }
        //设置读取到数据的高低位
        // _plc.ByteTransform.IsStringReverseByteWord = true;
        //_plc.ByteTransform.DataFormat = HslCommunication.Core.DataFormat.DCBA;
        //_plc.ByteTransform.DataFormat = HslCommunication.Core.DataFormat.ABCD;

        _plc.IpAddress = ip;
        _plc.Port = port;
        try
        {
            var connect = _plc.ConnectServer();
            InitFlag = connect.IsSuccess;

            return connect;
        }
        catch (Exception ex)
        {
            operateResult.IsSuccess = false;
            operateResult.Message = ex.Message;
            return operateResult;
        }
    }

    #endregion

    #endregion

    #region 直接Copy的方法

    #region 关闭连接
    public override OperateResult ClosePlc()
    {
        try
        {
            return _plc.ConnectClose();
        }
        catch (Exception ex)
        {
            OperateResult operateResult = new OperateResult();
            operateResult.IsSuccess = false;
            operateResult.Message = ex.Message;
            return operateResult;
        }
    }
    #endregion

    #region 读取PLC

    public override OperateResult ReadAll(List<PlcAddressInfo> plcAddressInfoList)
    {

        OperateResult operateResult = new OperateResult();
        try
        {
            foreach (var plcAddressInfo in plcAddressInfoList)
            {
                if (plcAddressInfo.State == PlcStateType.默认值)
                {
                    plcAddressInfo.Value = plcAddressInfo.DefaultValue;
                    plcAddressInfo.ReadSuccess = false;
                    continue;
                }

                if (plcAddressInfo.State == PlcStateType.停用)
                {
                    plcAddressInfo.Value = "未启用";
                    plcAddressInfo.ReadSuccess = false;
                    continue;
                }

                if (string.IsNullOrEmpty(plcAddressInfo.Address))
                {
                    plcAddressInfo.Value = "地址未设置";
                    plcAddressInfo.ReadSuccess = false;
                    continue;
                }

                switch (plcAddressInfo.DataType)
                {
                    case PlcDataType.Bool1:
                        var operateResultBool = ReadBool(plcAddressInfo.Address);
                        if (operateResultBool.IsSuccess)
                        {
                            plcAddressInfo.Value = operateResultBool.Content + "";
                            plcAddressInfo.ReadSuccess = true;
                        }
                        else
                        {
                            plcAddressInfo.Value = operateResultBool.Message;
                            plcAddressInfo.ReadSuccess = false;
                        }

                        break;

                    case PlcDataType.Byte8:
                    case PlcDataType.Short16:
                        var operateResultShort = ReadShort(plcAddressInfo.Address);
                        if (operateResultShort.IsSuccess)
                        {
                            plcAddressInfo.Value = operateResultShort.Content + "";
                            plcAddressInfo.ReadSuccess = true;
                        }
                        else
                        {
                            plcAddressInfo.Value = operateResultShort.Message;
                            plcAddressInfo.ReadSuccess = false;
                        }

                        break;

                    case PlcDataType.UShort16:
                        var operateResultUshort = ReaduShort(plcAddressInfo.Address);
                        if (operateResultUshort.IsSuccess)
                        {
                            plcAddressInfo.Value = operateResultUshort.Content + "";
                            plcAddressInfo.ReadSuccess = true;
                        }
                        else
                        {
                            plcAddressInfo.Value = operateResultUshort.Message;
                            plcAddressInfo.ReadSuccess = false;
                        }

                        break;

                    case PlcDataType.Int32:
                        var operateResultInt = ReadInt(plcAddressInfo.Address);
                        if (operateResultInt.IsSuccess)
                        {
                            plcAddressInfo.Value = operateResultInt.Content + "";
                            plcAddressInfo.ReadSuccess = true;
                        }
                        else
                        {
                            plcAddressInfo.Value = operateResultInt.Message;
                            plcAddressInfo.ReadSuccess = false;
                        }

                        break;

                    case PlcDataType.UInt32:
                        var operateResultUint = ReaduInt(plcAddressInfo.Address);
                        if (operateResultUint.IsSuccess)
                        {
                            plcAddressInfo.Value = operateResultUint.Content + "";
                            plcAddressInfo.ReadSuccess = true;
                        }
                        else
                        {
                            plcAddressInfo.Value = operateResultUint.Message;
                            plcAddressInfo.ReadSuccess = false;
                        }

                        break;

                    case PlcDataType.Long64:
                        var operateResultLong = ReadLong(plcAddressInfo.Address);
                        if (operateResultLong.IsSuccess)
                        {
                            plcAddressInfo.Value = operateResultLong.Content + "";
                            plcAddressInfo.ReadSuccess = true;
                        }
                        else
                        {
                            plcAddressInfo.Value = operateResultLong.Message;
                            plcAddressInfo.ReadSuccess = false;
                        }

                        break;

                    case PlcDataType.ULong64:
                        var operateResultUlong = ReaduLong(plcAddressInfo.Address);
                        if (operateResultUlong.IsSuccess)
                        {
                            plcAddressInfo.Value = operateResultUlong.Content + "";
                            plcAddressInfo.ReadSuccess = true;
                        }
                        else
                        {
                            plcAddressInfo.Value = operateResultUlong.Message;
                            plcAddressInfo.ReadSuccess = false;
                        }

                        break;

                    case PlcDataType.Float单精度:
                        var operateResultFloat = ReadFloat(plcAddressInfo.Address);
                        if (operateResultFloat.IsSuccess)
                        {
                            plcAddressInfo.Value = operateResultFloat.Content + "";
                            plcAddressInfo.ReadSuccess = true;
                        }
                        else
                        {
                            plcAddressInfo.Value = operateResultFloat.Message;
                            plcAddressInfo.ReadSuccess = false;
                        }

                        break;

                    case PlcDataType.Doubles双精度:
                        var operateResultDouble = ReadDouble(plcAddressInfo.Address);
                        if (operateResultDouble.IsSuccess)
                        {
                            plcAddressInfo.Value = operateResultDouble.Content + "";
                            plcAddressInfo.ReadSuccess = true;
                        }
                        else
                        {
                            plcAddressInfo.Value = operateResultDouble.Message;
                            plcAddressInfo.ReadSuccess = false;
                        }

                        break;
                    case PlcDataType.String字符串:
                        OperateResult<string> operateResultString = ReadString(plcAddressInfo.Address,
                            (ushort)plcAddressInfo.Length, plcAddressInfo.EncodingType);
                        if (operateResultString.IsSuccess)
                        {
                            plcAddressInfo.Value = operateResultString.Content + "";
                            plcAddressInfo.ReadSuccess = true;
                        }
                        else
                        {
                            plcAddressInfo.Value = operateResultString.Message;
                            plcAddressInfo.ReadSuccess = false;
                        }

                        break;
                    default:
                        plcAddressInfo.ReadSuccess = false;
                        plcAddressInfo.Value = "数据类型错误";
                        break;
                }
            }

            operateResult.IsSuccess = true;
            return operateResult;
        }
        catch (Exception ex)
        {
            operateResult.IsSuccess = false;
            operateResult.Message = ex.Message;
            return operateResult;
        }

    }

    public override OperateResult<bool> ReadBool(string address)
    {
        var operateResult = _plc.ReadBool(address);
        return operateResult;
    }


    public override OperateResult<short> ReadShort(string address)
    {
        var operateResult = _plc.ReadInt16(address);
        return operateResult;
    }

    public override OperateResult<ushort> ReaduShort(string address)
    {
        var operateResult = _plc.ReadUInt16(address);
        return operateResult;
    }


    public override OperateResult<int> ReadInt(string address)
    {
        var operateResult = _plc.ReadInt32(address);
        return operateResult;
    }

    public override OperateResult<uint> ReaduInt(string address)
    {
        var operateResult = _plc.ReadUInt32(address);
        return operateResult;
    }


    public override OperateResult<long> ReadLong(string address)
    {
        var operateResult = _plc.ReadInt64(address);
        return operateResult;
    }


    public override OperateResult<ulong> ReaduLong(string address)
    {
        var operateResult = _plc.ReadUInt64(address);
        return operateResult;
    }

    public override OperateResult<float> ReadFloat(string address)
    {
        var operateResult = _plc.ReadFloat(address);
        return operateResult;
    }


    public override OperateResult<double> ReadDouble(string address)
    {
        var operateResult = _plc.ReadDouble(address);
        return operateResult;
    }

    /// <summary>
    ///     1.读取字符串的方法，西门子PLC不需要长度，三菱的必须要
    ///     2.这个方法的长度应该是最大长度，猜测的
    ///     3.因为要长度，没有开始两位代表字节长度，所以会有后面补全的情况
    /// </summary>
    /// <param name="address"></param>
    /// <param name="length"></param>
    /// <param name="encodingType"></param>
    /// <returns></returns>
    public override OperateResult<string> ReadString(string address, ushort length = 0, EncodingType encodingType = EncodingType.None)
    {

        OperateResult<string> operateResult;
        switch (encodingType)
        {
            case EncodingType.None:
                operateResult = _plc.ReadString(address, length);
                break;
            case EncodingType.Default:
            case EncodingType.Ansi:
            case EncodingType.Gb2312:
                operateResult = _plc.ReadString(address, length, Encoding.Default);
                break;
            case EncodingType.Utf8:
                operateResult = _plc.ReadString(address, length, Encoding.UTF8);
                break;
            case EncodingType.Ascii:
                operateResult = _plc.ReadString(address, length, Encoding.ASCII);
                break;
            case EncodingType.Unicode:
                operateResult = _plc.ReadString(address, length, Encoding.Unicode);
                break;
            case EncodingType.BigEndianUnicode:
                operateResult = _plc.ReadString(address, length, Encoding.BigEndianUnicode);
                break;
            case EncodingType.Utf32:
                operateResult = _plc.ReadString(address, length, Encoding.UTF32);
                break;

            default:
                operateResult = _plc.ReadString(address, length);
                break;
        }

        //不足位会补全 字符串的结束符(即\0)
        //operateResult.Content 有时为null，有时为\0，有时为"",需注意
        if (!string.IsNullOrEmpty(operateResult.Content))
            operateResult.Content = operateResult.Content.Replace("\0", "").Trim();
        return operateResult;
    }

    #endregion

    #region 写入PLC

    public override OperateResult WritePlc(PlcAddressInfo plcAddressInfo, string value)
    {
        try
        {
            switch (plcAddressInfo.DataType)
            {
                case PlcDataType.Bool1:
                    return WriteBool(plcAddressInfo.Address, Convert.ToBoolean(value));
                case PlcDataType.Byte8:
                    return WriteByte(plcAddressInfo.Address, Convert.ToByte(value));
                case PlcDataType.Short16:
                    return WriteShort(plcAddressInfo.Address, Convert.ToInt16(value));
                case PlcDataType.UShort16:
                    return WriteuShort(plcAddressInfo.Address, Convert.ToUInt16(value));
                case PlcDataType.Int32:
                    return WriteInt(plcAddressInfo.Address, Convert.ToInt32(value));
                case PlcDataType.UInt32:
                    return WriteuInt(plcAddressInfo.Address, Convert.ToUInt32(value));
                case PlcDataType.Long64:
                    return WriteLong(plcAddressInfo.Address, Convert.ToInt64(value));
                case PlcDataType.ULong64:
                    return WriteuLong(plcAddressInfo.Address, Convert.ToUInt64(value));
                case PlcDataType.Float单精度:
                    return WriteFloat(plcAddressInfo.Address, Convert.ToSingle(value));
                case PlcDataType.Doubles双精度:
                    return WriteDouble(plcAddressInfo.Address, Convert.ToDouble(value));
                case PlcDataType.String字符串:
                    return WriteString(plcAddressInfo.Address, value, plcAddressInfo.Length, plcAddressInfo.EncodingType);
                default:
                    OperateResult operateResult = new OperateResult();
                    operateResult.IsSuccess = false;
                    operateResult.Message = "数据类型不存在";
                    return operateResult;
            }
        }
        catch (Exception ex)
        {
            OperateResult operateResult = new OperateResult();
            operateResult.IsSuccess = false;
            operateResult.Message = ex.Message;
            return operateResult;
        }
    }


    public override OperateResult WriteBool(string address, bool value)
    {
        return _plc.Write(address, value);
    }


    public override OperateResult WriteByte(string address, byte value)
    {
        return _plc.Write(address, value);
    }

    public override OperateResult WriteShort(string address, short value)
    {
        return _plc.Write(address, value);
    }

    public override OperateResult WriteuShort(string address, ushort value)
    {
        return _plc.Write(address, value);
    }

    public override OperateResult WriteInt(string address, int value)
    {
        return _plc.Write(address, value);
    }

    public override OperateResult WriteuInt(string address, uint value)
    {
        return _plc.Write(address, value);
    }

    public override OperateResult WriteLong(string address, long value)
    {
        return _plc.Write(address, value);
    }

    public override OperateResult WriteuLong(string address, ulong value)
    {
        return _plc.Write(address, value);
    }

    public override OperateResult WriteFloat(string address, float value)
    {
        return _plc.Write(address, value);
    }

    public override OperateResult WriteDouble(string address, double value)
    {
        return _plc.Write(address, value);
    }

    public override OperateResult WriteString(string address, string value, int length = 0, EncodingType encodingType = EncodingType.None)
    {
        OperateResult operateResult;
        switch (encodingType)
        {
            case EncodingType.None:
                operateResult = length <= 0 ?
                    _plc.Write(address, value) :
                    _plc.Write(address, value, length);
                break;
            case EncodingType.Default:
            case EncodingType.Ansi:
            case EncodingType.Gb2312:
                operateResult = length <= 0 ?
                    _plc.Write(address, value, Encoding.Default) :
                    _plc.Write(address, value, length, Encoding.Default);
                break;
            case EncodingType.Utf8:
                operateResult = length <= 0 ?
                    _plc.Write(address, value, Encoding.UTF8) :
                    _plc.Write(address, value, length, Encoding.UTF8);
                break;
            case EncodingType.Ascii:
                operateResult = length <= 0 ?
                    _plc.Write(address, value, Encoding.ASCII) :
                    _plc.Write(address, value, length, Encoding.ASCII);
                break;
            case EncodingType.Unicode:
                operateResult = length <= 0 ?
                    _plc.Write(address, value, Encoding.Unicode) :
                    _plc.Write(address, value, length, Encoding.Unicode);
                break;
            case EncodingType.BigEndianUnicode:
                operateResult = length <= 0 ?
                    _plc.Write(address, value, Encoding.BigEndianUnicode) :
                    _plc.Write(address, value, length, Encoding.BigEndianUnicode);
                break;
            case EncodingType.Utf32:
                operateResult = length <= 0 ?
                    _plc.Write(address, value, Encoding.UTF32) :
                    _plc.Write(address, value, length, Encoding.UTF32);
                break;
            default:
                operateResult = length <= 0 ?
                    _plc.Write(address, value) :
                    _plc.Write(address, value, length);
                break;
        }
        return operateResult;
    }

    #endregion


    #endregion

}